#include "EPhScene.h"
#include "EPhysics.h"
#include "EPhControllerManager.h"
#include "eCPP.h"

namespace engine
{


//------------------------------------------------------------
EPhSceneDesc::EPhSceneDesc() :
m_vGravity( 0.f, -9.8f, 0.f )
{
}
//------------------------------------------------------------
const NxSceneDesc EPhSceneDesc::getNxSceneDesc() const
{
  NxSceneDesc nxDesc;
  nxDesc.gravity.set( m_vGravity.getNxVec3() );

  return nxDesc;
}


//------------------------------------------------------------
// EPhScene
//------------------------------------------------------------

const float EPhScene::s_fDefaultTimeStep      = 1.f / EGameTime::s_fDefaultFPS;
const int   EPhScene::s_iNbSubSteps           = 4;
//------------------------------------------------------------
EPhScene::EPhScene(const EPhSceneDesc& sceneDesc ) :
m_SceneDesc( sceneDesc ),
m_pNxScene( NULL ),
m_pPhControllerManager( NULL )
{
  
}

//------------------------------------------------------------
EPhScene::~EPhScene()
{
  release();

  // todo release actors & materials
}

//------------------------------------------------------------
bool EPhScene::init()
{
  cASSERT( g_pPhysics != NULL );
  m_pNxScene = g_pPhysics->getNxSDK().createScene( m_SceneDesc.getNxSceneDesc() );
  cTEST( m_pNxScene != NULL );

  m_pPhControllerManager = new EPhControllerManager();
  cTEST( m_pPhControllerManager != NULL );

  return true;
}

//------------------------------------------------------------
void EPhScene::release()
{
  if( m_pNxScene != NULL )
  {
    cASSERT( g_pPhysics != NULL );

    if( m_pPhControllerManager != NULL )
    {
      delete m_pPhControllerManager;
      m_pPhControllerManager = NULL;
    }

    g_pPhysics->getNxSDK().releaseScene( *m_pNxScene );
    m_pNxScene = NULL;

    for( vector<EPhActor*>::iterator it = m_vActors.begin(); it!= m_vActors.end(); ++it )
    {
      delete *it;
    }
    m_vActors.clear();

    for( vector<EPhMaterial*>::iterator it = m_vMaterials.begin(); it!= m_vMaterials.end(); ++it )
      delete *it;
    m_vMaterials.clear();
  }
  else
  {
    // if Nx scene is empty, no actors should be in the
    cASSERT( m_vActors.size() == 0 );
    cASSERT( m_vMaterials.size() == 0 );
  }
}

//------------------------------------------------------------
bool EPhScene::startSimulationComputing(const EGameTime& gameTime)
{
  cASSERT( m_pNxScene != NULL );
  cASSERT( g_pPhysics != NULL );

  m_pNxScene->setTiming( s_fDefaultTimeStep/(float)s_iNbSubSteps, s_iNbSubSteps, NX_TIMESTEP_FIXED );
  m_pNxScene->simulate( gameTime.m_fDT );
  

  return true;
}

//------------------------------------------------------------
bool EPhScene::endSimulationComputing()
{
  cASSERT( m_pNxScene != NULL );
  cASSERT( g_pPhysics != NULL );

  /*if( !isSimulationComputingEnded() )
  {*/
  m_pNxScene->flushStream();
    m_pNxScene->fetchResults(NX_RIGID_BODY_FINISHED, true);
  //}
  if( m_pPhControllerManager != NULL )
    m_pPhControllerManager->updateControllers();

  return true;
}

//------------------------------------------------------------
bool EPhScene::isSimulationComputingEnded() const
{
  cASSERT( m_pNxScene != NULL );
  cASSERT( g_pPhysics != NULL );

  return m_pNxScene->fetchResults(NX_RIGID_BODY_FINISHED, false);
}

//------------------------------------------------------------
void EPhScene::setGravity(const EVector& vGravity )
{
  cASSERT( m_pNxScene != NULL );
  m_pNxScene->setGravity( vGravity.getNxVec3() );
}

//------------------------------------------------------------
EPhActor* EPhScene::createActor(const EPhActorDesc& actorDesc )
{
  cASSERT( m_pNxScene != NULL );

  const NxActorDesc &nxActorDesc = actorDesc.getNxActorDesc();
  cTEST( nxActorDesc.isValid() );
  NxActor* pNxActor = m_pNxScene->createActor( nxActorDesc );
  cASSERT( pNxActor != NULL );
  if( pNxActor == NULL )
    return NULL;
  /*NX_DBG_CREATE_OBJECT(pNxActor, NX_DBG_OBJECTTYPE_ACTOR, "actor", 0x00100000);
  NX_DBG_CREATE_PARAMETER(NxVec3 (1.2f, 0.2f, 0.1f), pNxActor, "Center of mass", 0x00100000);*/
  EPhActor *pActor = new EPhActor( pNxActor );
  cASSERT( pActor != NULL );
  pNxActor->userData = pActor;

  m_vActors.push_back( pActor );
  return pActor;
}

//------------------------------------------------------------
void EPhScene::destroyActor(EPhActor& actor )
{
  m_pNxScene->releaseActor( *actor.getNxActorPtr() );
  for( vector<EPhActor*>::iterator it = m_vActors.begin(); it != m_vActors.end(); ++it )
  {
    if( *it == &actor )
    {
      m_vActors.erase( it );
      break;
    }
  }
}

//------------------------------------------------------------
EPhController*  EPhScene::createController(const EPhControllerDesc& controllerDesc )
{
  cASSERT( m_pPhControllerManager != NULL );

  return m_pPhControllerManager->createController( *this, controllerDesc ); 
}

//------------------------------------------------------------
void EPhScene::destroyController(EPhController& controller )
{
  cASSERT( m_pPhControllerManager != NULL );

  m_pPhControllerManager->destroyController( controller );
}

//------------------------------------------------------------
EPhMaterial* EPhScene::createMaterial(const EPhMaterialDesc& matDesc )
{
  cASSERT( m_pNxScene != NULL );

  cTEST( matDesc.getNxMaterialDesc().isValid() );

  NxMaterial* pNxMaterial = m_pNxScene->createMaterial( matDesc.getNxMaterialDesc() );
  cASSERT( pNxMaterial != NULL );
  if( pNxMaterial == NULL )
    return NULL;

  EPhMaterial*    pMaterial = new EPhMaterial( *pNxMaterial );
  cASSERT( pMaterial != NULL );

  m_vMaterials.push_back( pMaterial );
  return pMaterial;
}
} // end namespace